//-----------------------------------------------------------------------------
//
// Config.c
//
// Group:
//    Doug Kline,          dakline@ncsu.edu
//    Mauricio Del Carpio, mgdelcar@ncsu.edu
//    Josh Jodrey,         jdjodrey@ncsu.edu
//
//
// Sets the configuration /prof files and then reads the entries in and parses.
//-----------------------------------------------------------------------------


#include <linux/module.h>       /* Needed by all modules */
#include <linux/string.h>
#include <linux/kernel.h>       /* Needed for KERN_INFO */
#include <linux/inet.h>

#include "config.h"

#define MODULE_VERS "1.0"
#define MODULE_NAME "kcache"


extern struct Config g_config;
extern int info;

//
// Proc filesystem example from
// linux-2.6.31/Documentation/DocBook/procfs_example.c
//
// Writeup:  www.ibm.com/developerworks/linux/library/l-proc.html

// The proc structure to store the cache size
struct ProxyAddr
{
	char name[BUFSIZE + 1];
	char value[BUFSIZE + 1];
};

struct ProxyPort
{
	char name[BUFSIZE + 1];
	char value[BUFSIZE + 1];
};

struct CacheSize
{
	char name[BUFSIZE + 1];
	char value[BUFSIZE + 1];
};

// The proc structure to store the cache time
struct CacheTime
{
	char name[BUFSIZE + 1];
	char value[BUFSIZE + 1];
};


// Pointers to store the created proc file nodes
static struct proc_dir_entry *g_kcache_dir   = NULL;
static struct proc_dir_entry *g_addr_file    = NULL;
static struct proc_dir_entry *g_port_file    = NULL;
static struct proc_dir_entry *g_size_file    = NULL;
static struct proc_dir_entry *g_time_file    = NULL;


// Instances of the configuration data
struct ProxyAddr  g_proxyAddr;
struct ProxyPort  g_proxyPort;
struct CacheSize  g_cacheSize;
struct CacheTime  g_cacheTime;

//----------------------------------------------------------------------------
//
// cleanStr()
//
// Make sure there are no funky characters
//
//----------------------------------------------------------------------------
static void cleanStr(char *str)
{
   while(*str != '\0')
   {
      if( (*str == '\n') ||
          (*str == '\r') ||
          (*str == '\t')    )
      {
         *str = '\0';
         break;
      }
      str++;
   }

} // end cleanStr()


//----------------------------------------------------------------------------
//
// procReadProxyAddr()
//
// The read routine for the proxy address proc entry.
//----------------------------------------------------------------------------
static int procReadProxyAddr(char *page, char **start,
			    off_t off, int count, 
			    int *eof, void *data)
{
	struct ProxyAddr *data_p = (struct ProxyAddr *)data;

   // Everything is being returned in one shot
   *eof = 1;
   
   // page is in kernel space so copy to it
	strncpy(page, data_p->value, count);
	page[count] = '\0';
	cleanStr(page);
   if(info) printk(KERN_INFO "procReadProxyAddr=%s\n", page);
	return strlen(page);
}



//----------------------------------------------------------------------------
//
// procWriteProxyAddr()
//
// The write routine for the proxy address proc entry.
//----------------------------------------------------------------------------
static int procWriteProxyAddr(struct file *file,
			     const char *buffer,
			     unsigned long count, 
			     void *data)
{
	int len;
	struct ProxyAddr *data_p = (struct ProxyAddr *)data;

	if(count > ADDR_LEN)
		len = ADDR_LEN;
	else
		len = count;

	if(copy_from_user(data_p->value, buffer, len))
		return -EFAULT;

	data_p->value[len] = '\0';
   cleanStr(data_p->value);
   if(info) printk(KERN_INFO "procWriteProxyAddr=%s\n", data_p->value);

	return len;
	
} // end procWriteProxyAddr()



//----------------------------------------------------------------------------
//
// procReadProxyPort()
//
// The read routine for the proxy port proc entry.
//----------------------------------------------------------------------------
static int procReadProxyPort(char *page, char **start,
			    off_t off, int count, 
			    int *eof, void *data)
{
	struct ProxyPort *data_p = (struct ProxyPort *)data;

   // Everything is being returned in one shot
   *eof = 1;
   
   // page is in kernel space so copy to it
	strncpy(page, data_p->value, count);
	page[count] = '\0';
	cleanStr(page);
   if(info) printk(KERN_INFO "procReadProxyPort=%s\n", page);
	return strlen(page);
}


//----------------------------------------------------------------------------
//
// procWriteProxyPort()
//
// The write routine for the proxy port proc entry.
//----------------------------------------------------------------------------
static int procWriteProxyPort(struct file *file,
			     const char *buffer,
			     unsigned long count, 
			     void *data)
{
	int len;
	struct ProxyPort *data_p = (struct ProxyPort *)data;

	if(count > PORT_LEN)
		len = PORT_LEN;
	else
		len = count;

	if(copy_from_user(data_p->value, buffer, len))
		return -EFAULT;

	data_p->value[len] = '\0';
   cleanStr(data_p->value);
   if(info) printk(KERN_INFO "procWriteProxyPort=%s\n", data_p->value);

	return len;
	
} // end procWriteProxyPort()



//----------------------------------------------------------------------------
//
// procReadSize()
//
// The read routine for the cache size proc entry.
//----------------------------------------------------------------------------
static int procReadSize(char *page, char **start,
			    off_t off, int count, 
			    int *eof, void *data)
{
	struct CacheSize *data_p = (struct CacheSize *)data;

   // Everything is being returned in one shot
   *eof = 1;
   
   // page is in kernel space so copy to it
	strncpy(page, data_p->value, count);
	page[count] = '\0';
	cleanStr(page);
   if(info) printk(KERN_INFO "procReadSize=%s\n", page);
	return strlen(page);
}


//----------------------------------------------------------------------------
//
// procWriteSize()
//
// The write routine for the cache size proc entry.
//----------------------------------------------------------------------------
static int procWriteSize(struct file *file,
			     const char *buffer,
			     unsigned long count, 
			     void *data)
{
	int len;
	struct CacheSize *data_p = (struct CacheSize *)data;

	if(count > SIZE_LEN)
		len = SIZE_LEN;
	else
		len = count;

	if(copy_from_user(data_p->value, buffer, len))
		return -EFAULT;

	data_p->value[len] = '\0';
   cleanStr(data_p->value);
   if(info) printk(KERN_INFO "procWriteSize=%s\n", data_p->value);

	return len;
	
} // end procWriteSize()


//----------------------------------------------------------------------------
//
// procReadTime()
//
// The read routine for the cache time proc entry.
//----------------------------------------------------------------------------
static int procReadTime(char *page, char **start,
			    off_t off, int count, 
			    int *eof, void *data)
{
	struct CacheTime *data_p = (struct CacheTime *)data;

   // Everything is being returned in one shot
   *eof = 1;
   
   // page is in kernel space so copy to it
	strncpy(page, data_p->value, count);
	page[count] = '\0';
	cleanStr(page);
   if(info) printk(KERN_INFO "procReadTime=%s\n", page);
	return strlen(page);
}


//----------------------------------------------------------------------------
//
// procWriteTime()
//
// The write routine for the cache time proc entry.
//----------------------------------------------------------------------------
static int procWriteTime(struct file *file,
			     const char *buffer,
			     unsigned long count, 
			     void *data)
{
	int len;
	struct CacheTime *data_p = (struct CacheTime *)data;

	if(count > TIME_LEN)
		len = TIME_LEN;
	else
		len = count;

	if(copy_from_user(data_p->value, buffer, len))
		return -EFAULT;

	data_p->value[len] = '\0';
   cleanStr(data_p->value);
   if(info) printk(KERN_INFO "procWriteTime=%s\n", data_p->value);

	return len;
	
} // end procWriteTime()




//----------------------------------------------------------------------------
//
// initProcFile()
//
// Initialize the proc file entries
//
//----------------------------------------------------------------------------
int initProcFile(void)
{

   int err = 0;
   
	// Create "top" directory
	g_kcache_dir = proc_mkdir(MODULE_NAME, NULL);
	
	if(g_kcache_dir == NULL)
   {
		err = -ENOMEM;
		goto out;
	}

   //------------------------------------------------------------------------------
   // Create the proxy address
   
	g_addr_file = create_proc_entry("proxyAddr", 0644, g_kcache_dir);
	if(g_addr_file == NULL)
	{
		err = -ENOMEM;
		goto no_addr;
	}

	strcpy(g_proxyAddr.name, "proxyAddr");
	strcpy(g_proxyAddr.value, "192.168.0.1");
	g_addr_file->data       = &g_proxyAddr;
	g_addr_file->read_proc  = procReadProxyAddr;
	g_addr_file->write_proc = procWriteProxyAddr;
		
	
   //------------------------------------------------------------------------------
   // Create the proxy port
   
	g_port_file = create_proc_entry("proxyPort", 0644, g_kcache_dir);
	if(g_port_file == NULL)
	{
		err = -ENOMEM;
		goto no_port;
	}

	strcpy(g_proxyPort.name, "proxyPort");
	strcpy(g_proxyPort.value, "65000");
	g_port_file->data       = &g_proxyPort;
	g_port_file->read_proc  = procReadProxyPort;
	g_port_file->write_proc = procWriteProxyPort;
		
	
   //------------------------------------------------------------------------------
   // Create the cache size
   
	g_size_file = create_proc_entry("size", 0644, g_kcache_dir);
	if(g_size_file == NULL)
	{
		err = -ENOMEM;
		goto no_size;
	}

	strcpy(g_cacheSize.name, "size");
	strcpy(g_cacheSize.value, "16384");
	g_size_file->data       = &g_cacheSize;
	g_size_file->read_proc  = procReadSize;
	g_size_file->write_proc = procWriteSize;
		
	
   //------------------------------------------------------------------------------
   // Create the cache time
   
	g_time_file = create_proc_entry("time", 0644, g_kcache_dir);
	if(g_time_file == NULL)
	{
		err = -ENOMEM;
		goto no_time;
	}

	strcpy(g_cacheTime.name, "time");
	strcpy(g_cacheTime.value, "3600");
	g_time_file->data       = &g_cacheTime;
	g_time_file->read_proc  = procReadTime;
	g_time_file->write_proc = procWriteTime;
	
   /* everything OK */
	if(info) printk(KERN_INFO "kcache proc file system initialized\n");
 
   return(0);
   
no_time:
	remove_proc_entry("size", g_kcache_dir);
no_size:
	remove_proc_entry("proxyPort", g_kcache_dir);
no_port:   
	remove_proc_entry("proxyAddr", g_kcache_dir);
no_addr:
	remove_proc_entry(MODULE_NAME, NULL);
out:
   printk(KERN_ERR "ERROR: failed to intialize /proc files\n");
   return err;
   
} // initProcFile()


//----------------------------------------------------------------------------
//
// closeProcFile()
//
// Close down the proc file entries
//
//----------------------------------------------------------------------------
int closeProcFile(void)
{

	remove_proc_entry("time",      g_kcache_dir);
	remove_proc_entry("size",      g_kcache_dir);
	remove_proc_entry("proxyPort", g_kcache_dir);
	remove_proc_entry("proxyAddr", g_kcache_dir);
	remove_proc_entry(MODULE_NAME, NULL);

	if(info) printk(KERN_INFO "kcache proc file system removed\n");

   return(0);
   
} // end closeProcFile()


//----------------------------------------------------------------------------
//
// atoi()
//
// Quick and dirty ascii to integer
//
//----------------------------------------------------------------------------
static int atoi(char *p)
{
   // 32-bit can be upto 11 decimal digits
   int count = 0;
   int value = 0;
   int ok    = 1;
   
   while( p  && ok && (count < 11))
   {
       value = 10 * value;

       switch(*p)
       {
          case '0': value += 0; break;
          case '1': value += 1; break;
          case '2': value += 2; break;
          case '3': value += 3; break;
          case '4': value += 4; break;
          case '5': value += 5; break;
          case '6': value += 6; break;
          case '7': value += 7; break;
          case '8': value += 8; break;
          case '9': value += 9; break;
          default:
          {
             ok = 0;
             value = value / 10;
          }
       }
       
       count++;
       p++;
   }
   
   return value;

} // end atoi()

//----------------------------------------------------------------------------
//
// getConfig()
//
// Get the configuration from the proc file system into internal memory.
//
//----------------------------------------------------------------------------
int getConfig(void)
{
   char buf[BUFSIZE + 1];  // This needs to be long enough
   int eof = 0;
  
   // Set defaults
   g_config.proxyPort        = 0;
   g_config.proxyAddr.s_addr = 0;    
   g_config.cacheSize        = 512; /* 512 KByte */
   g_config.cacheTime        = 60;  /* seconds */

   // Get the proxy address
   procReadProxyAddr(buf, NULL, 0, BUFSIZE-1, &eof, &g_proxyAddr);  
   g_config.proxyAddr.s_addr = in_aton(buf);
   
   // Get the proxy port
   procReadProxyPort(buf, NULL, 0, BUFSIZE-1, &eof, &g_proxyPort);  
   g_config.proxyPort = atoi(buf);
  
   // Get the cache size
   procReadSize(buf, NULL, 0, BUFSIZE-1, &eof, &g_cacheSize);  
   g_config.cacheSize = atoi(buf);

   // Get the cache time
   procReadTime(buf, NULL, 0, BUFSIZE-1, &eof, &g_cacheTime);  
   g_config.cacheTime = atoi(buf);

   if(info) printk(KERN_INFO "proxyAddr = 0x%8x\n", g_config.proxyAddr.s_addr);
   if(info) printk(KERN_INFO "proxyPort = %d\n", g_config.proxyPort);
   if(info) printk(KERN_INFO "time = %d\n", g_config.cacheTime);
   if(info) printk(KERN_INFO "size = %d\n", g_config.cacheSize);

   return 0;

} // end getConfig()


